package com.cmwa.solution.utils.reverseEngine.other;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import com.cmwa.solution.utils.DateUtils;

/**
 * 反向工程生成实体、service、dao、mapper
 * @author ex-hejm
 * @date 2018年10月18日 下午4:15:28
 */
public class GenerateUtil {
	private Connection connection;
	private Statement statement;
	
	private static String workspace = System.getProperty("user.dir");
	private static String templateDir = "\\src\\main\\webapp\\WEB-INF\\velocity\\reverseEngine\\";  // 模板文件位置
    private static String sourcePath = workspace + templateDir;
    private static String resultDir = "\\out";
    private static String targetPath = workspace + resultDir + "\\";
    private static String packageName = null;
    private static String moduleName = null;
    private static GenerateSql[] generateSql = null;
	
	private static final String COLUMN_NAME = "columnName";
	private static final String COLUMN_COMMENT = "columnComment";
	private static final String TABLE_COMMENT = "tableComment";
	
	private static final String DB_DRIVER = "oracle.jdbc.driver.OracleDriver";
	private static final String DB_URL = "jdbc:oracle:thin:@192.168.8.200:1521:DEVCMFBP";
	private static final String DB_USER = "CMWA_SOL";
	private static final String DB_PASSWORD = "cmf+2011";
	
	public static void generate(List<String> tableNames, String packageName1, String moduleName1, GenerateFile[] generateFiles, GenerateSql[] generateSql1, String author) throws Exception {
		packageName = packageName1;
		targetPath += packageName.replace(".", "\\");
		targetPath += "\\" + moduleName1.replace(".", "\\");
		moduleName = moduleName1;
		generateSql = generateSql1;
		
		GenerateUtil instance = new GenerateUtil();
		instance.initConnection(DB_DRIVER, DB_URL, DB_USER, DB_PASSWORD);
		
		for (String tableName : tableNames) {
			Map<String, List<Map<String, String>>> resultMap = instance.readAllTableAndColumns(tableName);
			Set<String> tables = resultMap.keySet();  // 所有的表
			for (String tn : tables) {
				generateFile(tn, resultMap.get(tn), generateFiles, author);
			}
		}
		instance.closeConnect();
	}
	
	/**
	 * 生成文件
	 * @author 何家明 2018年6月30日 下午7:05:35
	 * @param tableName
	 * @param list
	 * @param generateFile 
	 * @param author 
	 */
	private static void generateFile(String tableName, List<Map<String, String>> list, GenerateFile[] generateFiles, String author) {
		// 去掉表前缀
		String noPreTableName = tableName.toLowerCase().replaceFirst(tableName.toLowerCase().split("_")[0] + "_", "");
		// Java的类名
		String className = toJavaClassName(noPreTableName);
		// java类名首字母小写
		String pClassName = toLowerCaseFirstOne(className);
		// 子包路径
//		String subPackage = pClassName;
		
		// 类注释
		String tableComment = list.get(0).get(TABLE_COMMENT);
		String nowStr = DateUtils.getNowStr(null);
		
		Map<String,Object> map = new HashMap<String,Object>();
		for (GenerateFile generateFile : generateFiles) {
			if(generateFile == GenerateFile.CONTROLLER || generateFile == GenerateFile.ALL) {
				map.put("CONTROLLER.vm","/controller/" + className + "Controller.java");
			}
			if(generateFile == GenerateFile.DAO || generateFile == GenerateFile.ALL) {
				map.put("DAO.vm","/dao/" + className + "Dao.java");
			}
			if(generateFile == GenerateFile.ENTITY || generateFile == GenerateFile.ALL) {
				map.put("ENTITY.vm","/entity/" + className + "Vo.java");
			}
			if(generateFile == GenerateFile.MAPPER || generateFile == GenerateFile.ALL) {
				map.put("MAPPER.vm","/mapper/" + className + "Dao.xml");
			}
			if(generateFile == GenerateFile.SERVICE_IMPL || generateFile == GenerateFile.ALL) {
				map.put("SERVICE_IMPL.vm","/service/impl/" + className + "ServiceImpl.java");
			}
			if(generateFile == GenerateFile.SERVICE || generateFile == GenerateFile.ALL) {
				map.put("SERVICE.vm","/service/" + className + "Service.java");
			}
		}
		
		boolean generateInsert = false;
		boolean generateUpdate = false;
		boolean generateListPage = false;
		boolean generateSelectById = false;
		boolean generateLogicDelete = false;
		boolean generatePhysicDelete = false;
		boolean generateAll = false;
		for (GenerateSql gs : generateSql) {
			if(gs == GenerateSql.INSERT) {
				generateInsert = true;
			}
			if(gs == GenerateSql.UPDATE) {
				generateUpdate = true;
			}
			if(gs == GenerateSql.LIST_PAGE) {
				generateListPage = true;
			}
			if(gs == GenerateSql.SELECT_BY_ID) {
				generateSelectById = true;
			}
			if(gs == GenerateSql.LOGIC_DELETE) {
				generateLogicDelete = true;
			}
			if(gs == GenerateSql.PHYSIC_DELETE) {
				generatePhysicDelete = true;
			}
			if(gs == GenerateSql.ALL) {
				generateAll = true;
			}
		}
        
        for(String templateFile : map.keySet()){
            String targetFile = (String) map.get(templateFile);
            Properties pro = new Properties();
            pro.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
            pro.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
            pro.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, sourcePath);
            VelocityEngine ve = new VelocityEngine(pro);

            VelocityContext context = new VelocityContext();
            context.put("className", className);
            context.put("pClassName", pClassName);
//            context.put("subPackage", subPackage);
            context.put("moduleName", moduleName);
            context.put("packageName", packageName);
            context.put("nowStr", nowStr);
            context.put("generateInsert", generateInsert);
            context.put("generateUpdate", generateUpdate);
            context.put("generateListPage", generateListPage);
            context.put("generateSelectById", generateSelectById);
            context.put("generateLogicDelete", generateLogicDelete);
            context.put("generatePhysicDelete", generatePhysicDelete);
            context.put("generateAll", generateAll);
            context.put("tableComment", tableComment);
            context.put("author", author);
            context.put("seqName", "SEQ_" + noPreTableName.toUpperCase());
            context.put("tableName", tableName);
            
            // 属性声明
            List<Map<String, String>> propertyList = new ArrayList<Map<String,String>>();
 			for (Map<String, String> pMap : list) {
 				Map<String, String> property = new HashMap<String, String>();
 				
 				String columnName = pMap.get(COLUMN_NAME);
 				property.put("columnName", columnName);
 				String javaName = toJavaPropertyName(columnName.toLowerCase());
 				property.put("javaName", javaName);
 				
				String columnComment = pMap.get(COLUMN_COMMENT);
				property.put("columnComment", columnComment);
				String methodName = toJavaClassName(columnName.toLowerCase());
				property.put("methodName", methodName);
				
				propertyList.add(property);
 			}
 			context.put("propertyList", propertyList);

            Template t = ve.getTemplate(templateFile, "UTF-8");

            File file = new File(targetPath, targetFile);
            if (!file.getParentFile().exists())
                file.getParentFile().mkdirs();
            if (!file.exists()) {
            	try {
            		file.createNewFile();
            	} catch (IOException e) {
            		e.printStackTrace();
            	}
            }

            FileOutputStream outStream = null;
			try {
				outStream = new FileOutputStream(file);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			OutputStreamWriter writer = null;
			try {
				writer = new OutputStreamWriter(outStream, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
            BufferedWriter sw = new BufferedWriter(writer);
            try {
            	t.merge(context, sw);
			} finally {
				if(sw != null) {
					try {
						sw.flush();
					} catch (IOException e) {
						e.printStackTrace();
					}
					try {
						sw.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if(outStream != null) {
					try {
						outStream.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
            System.out.println("成功生成文件:" + (targetPath + targetFile).replaceAll("/", "\\\\"));
        }
	}

	/**
	 * 将helloWorld转换成hello_world
	 * @author 何家明 2018年6月30日 下午7:08:10
	 * @param fieldName
	 * @return
	 */
	public static String toDbName(String fieldName) {
        if (fieldName == null) {
            return "";
        }
        char[] chars = fieldName.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : chars) {
            if (CharUtils.isAsciiAlphaUpper(c)) {
                sb.append("_" + (char) (c + 32));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
	/**
	 * java属性名转换，将hello_world转换成helloWorld
	 * @author 何家明 2018年6月30日 下午7:08:10
	 * @param fieldName
	 * @return
	 */
	public static String toJavaPropertyName(String fieldName) {
        if (fieldName == null) {
            return "";
        }
        char[] chars = fieldName.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '_') {
            	if(i == chars.length - 1) {
            		break;
            	}
            	if(chars[i + 1] == '_') {
            		continue;
            	}
            	sb.append((char)(chars[++i] - 32));
            } else {
                sb.append(chars[i]);
            }
        }
        return sb.toString();
    }

	/**
	 * 首字母小写
	 * @author ex-hejm
	 * @date 2018年10月18日 下午6:49:02
	 * @param s
	 * @return
	 */
	public static String toLowerCaseFirstOne(String s) {
		if (Character.isLowerCase(s.charAt(0)))
			return s;
		else
			return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
	}
	/**
	 * java类名转换，将hello_world转换成HelloWorld
	 * @author 何家明 2018年6月30日 下午7:08:10
	 * @param fieldName
	 * @return
	 */
	private static String toJavaClassName(String fieldName) {
		String javaPropertyName = toJavaPropertyName(fieldName);
		if(StringUtils.isNotBlank(javaPropertyName)) {
			char[] ch = javaPropertyName.toCharArray();
		    if (ch[0] >= 'a' && ch[0] <= 'z') {
		        ch[0] = (char) (ch[0] - 32);
		    }
		    return new String(ch);
		}
		return "";
    }
	/**
	 * 读取数据库表信息
	 * @author 何家明 2018年6月30日 下午4:04:14
	 * @param tableName 
	 * @return
	 */
	public Map<String, List<Map<String, String>>> readAllTableAndColumns(String paramTableName) {
		Map<String, List<Map<String, String>>> resultMap = new HashMap<String, List<Map<String, String>>>();
		ResultSet rs = null;
		try {
			// 不考虑数据类型，全部按String处理
			String sql = "SELECT col_c.TABLE_NAME, col_c.COLUMN_NAME, col_c.COMMENTS COLUMN_COMMENT, tab_c.COMMENTS TABLE_COMMENT "
					   + "FROM USER_COL_COMMENTS col_c "
					   + "LEFT JOIN USER_TAB_COMMENTS tab_c ON tab_c.TABLE_NAME = col_c.TABLE_NAME ";
			if(StringUtils.isNotBlank(paramTableName)) {
				sql += "WHERE col_c.TABLE_NAME = '" + paramTableName.toUpperCase() + "'";
			}
			rs = this.statement.executeQuery(sql);
			while(rs.next()) {
				String tableName = rs.getString(1);
				String columnName = rs.getString(2);
				String columnComment = rs.getString(3);
				String tableComment = rs.getString(4);
				
				Map<String, String> columnMap = new HashMap<String, String>();
				columnMap.put(COLUMN_NAME, columnName);
				columnMap.put(COLUMN_COMMENT, columnComment);
				columnMap.put(TABLE_COMMENT, tableComment);
				
				List<Map<String, String>> list = resultMap.get(tableName);
				if(list == null) {
					List<Map<String, String>> columnList = new ArrayList<Map<String, String>>();
					columnList.add(columnMap);
					resultMap.put(tableName, columnList);
				} else {
					list.add(columnMap);
					resultMap.put(tableName, list);
				}
			}
			return resultMap;
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if(rs != null) {
					rs.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	public void initConnection(String driverClass, String dbUrl, String username, String password) throws Exception {
		Class.forName(driverClass);
		this.connection = DriverManager.getConnection(dbUrl, username, password);
		this.statement = this.connection.createStatement();
	}
	
	public void closeConnect() {
		try {
			if(this.statement != null) {
				this.statement.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		try {
			if(this.connection != null) {
				this.connection.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
